package com.cskaoyan.order.mq.delay;

import com.cskaoyan.mall.commons.exception.BaseBusinessException;
import com.cskaoyan.mall.commons.exception.BizException;
import com.cskaoyan.mall.order.constant.OrderRetCode;
import com.cskaoyan.order.constant.OrderConstants;
import com.cskaoyan.order.dal.entitys.Order;
import com.cskaoyan.order.dal.entitys.OrderItem;
import com.cskaoyan.order.dal.entitys.Stock;
import com.cskaoyan.order.dal.persistence.OrderItemMapper;
import com.cskaoyan.order.dal.persistence.OrderMapper;
import com.cskaoyan.order.dal.persistence.StockMapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.rocketmq.client.consumer.DefaultMQPushConsumer;
import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyContext;
import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyStatus;
import org.apache.rocketmq.client.consumer.listener.MessageListener;
import org.apache.rocketmq.client.consumer.listener.MessageListenerConcurrently;
import org.apache.rocketmq.client.exception.MQClientException;
import org.apache.rocketmq.common.message.MessageExt;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import tk.mybatis.mapper.entity.Example;

import javax.annotation.PostConstruct;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * DelayOrderCancel
 *
 * @param
 * @return
 * @2022/4/26
 **/
@Component
@Slf4j
public class DelayOrderCancelConsumer implements ApplicationContextAware {

    @Autowired
    OrderMapper orderMapper;
    @Autowired
    StockMapper stockMapper;

    @Autowired
    OrderItemMapper orderItemMapper;

    DefaultMQPushConsumer consumer;

    ApplicationContext applicationContext;

    @PostConstruct
    public void init() {
        consumer=new DefaultMQPushConsumer("delay_order_cancel_consumer");
        consumer.setNamesrvAddr("127.0.0.1:9876");
        consumer.setMessageListener(new MessageListenerConcurrently() {
            @Override
            public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> list, ConsumeConcurrentlyContext consumeConcurrentlyContext) {
//                "com.cskaoyan.order.mq.delay.DelayOrderCancelConsumer"
                try {
                    log.info("get DelayOrderCancelConsumer object ,for invoke cancelOrder method!");
                    DelayOrderCancelConsumer bean = (DelayOrderCancelConsumer) applicationContext.getBean(DelayOrderCancelConsumer.class);

                    bean.cancelOrder(list, consumeConcurrentlyContext);
                    return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
                } catch (Exception e) {
                    log.info("consumeMessage: cancelOrder method invoke exception!e={}", e.getMessage());
                    return ConsumeConcurrentlyStatus.RECONSUME_LATER;
//                    throw  new BizException(OrderRetCode.DB_EXCEPTION.getCode(),OrderRetCode.DB_EXCEPTION.getMessage());
                }
            }
        });
        try {
            log.info("before consumer start!");
            consumer.subscribe("delay_order_cancel", "*");
            consumer.start();
        } catch (MQClientException e) {
            log.info("Exception in consumer start! e={}",e.getErrorMessage());
            e.printStackTrace();
        }
    }

    //    check status!
    @Transactional
    public void cancelOrder(List<MessageExt> list, ConsumeConcurrentlyContext consumeConcurrentlyContext) {
        MessageExt msg = list.get(0);
//1.orderId
        byte[] body = msg.getBody();
        String orderId = new String(body, 0, body.length);

//                2.select order status
        Order order = orderMapper.selectByPrimaryKey(orderId);

//                3.status=1/5->do nothing

        int orderStatusPayed = OrderConstants.ORDER_STATUS_PAYED;
        if (orderStatusPayed == order.getStatus() || OrderConstants.ORDER_STATUS_TRANSACTION_CANCEL == order.getStatus()) {
            log.info("consumeMessage:3.status=1/5->do nothing!");
//                    TODO ConsumeConcurrentlyStatus
//            return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
//                    success code
        }
        if (OrderConstants.ORDER_STATUS_INIT == order.getStatus()) {
            log.info("consumeMessage:3.status=0->do all the things!");
//            4.status=0->do things
//                    1.order status=6
            order.setStatus(OrderConstants.ORDER_STATUS_TRANSACTION_CLOSE);
            order.setUpdateTime(new Date());
            order.setCloseTime(new Date());
            int update = orderMapper.updateByPrimaryKey(order);
            if (update < 1) {
                log.info("consumeMessage:order status modify exception!e={}",OrderRetCode.DB_EXCEPTION.getMessage());
                throw new BizException(OrderRetCode.DB_SAVE_EXCEPTION.getCode(),
                        OrderRetCode.DB_EXCEPTION.getMessage());
            }

//                    2.stock back to begin !
//                    orderId->orderItemId->itemId-----stock primary key!

            Example example=new Example(OrderItem.class);
            example.createCriteria().andEqualTo("orderId",orderId);
            List<OrderItem> orderItems = orderItemMapper.selectByExample(example);
            List<Long> orderItemIds=new ArrayList<>();
            for (OrderItem orderItem : orderItems) {
                Long itemId = orderItem.getItemId();
                orderItemIds.add(itemId);
            }
            List<Stock> stocksForUpdate = stockMapper.findStocksForUpdate(orderItemIds);
            if (orderItemIds.size() != stocksForUpdate.size()) {
                System.out.println("consumeMessage:part of stock has  not been inited!");
                throw new BaseBusinessException(OrderRetCode.DB_EXCEPTION.getCode(),
                        OrderRetCode.DB_EXCEPTION.getMessage());
            }
            for (Stock stock : stocksForUpdate) {
                stock.setLockCount(-stock.getLockCount());
                stock.setStockCount(Long.valueOf(stock.getLockCount()));
//                          lock_count = lock_count + #{lockCount},
//                          stock_count = stock_count + #{stockCount}
                stockMapper.updateStock(stock);
                log.info("consumeMessage:update stock!");
            }
//                    3.orderItem status =2
            orderItemMapper.updateStockStatus(2,orderId);
        }
//        TODO nothing to do! but status

//
    }


    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext=applicationContext;
    }
}
